#include "unitTest.h"
#include "TypeDefine.h"
#include "LargeIntegerMeta.h"
#include "RandGen.h"
#include "LIData.h"
#include "LIOperator.inl"

#include <time.h>
#include <vector>
#include <iostream>
using namespace std;

void run_unit_test()
{
	simple_rand randGen(time(NULL));
	const int test_size = 1e5;

	/////////////////////////////////////////
	// test Addition
	/////////////////////////////////////////
	{
		typedef UCHAR8 THETYPE;

		typedef LargeIntegerMeta<THETYPE,4> LITP;
		const int LITP_BITS = LITP::TOTAL_BITS;

		vector<UINT32> norm_in0(test_size), norm_in1(test_size), norm_out0(test_size);
		vector<LITP> input0(test_size), input1(test_size), output0(test_size);

		for (int i=0;i<test_size;i++)
		{
			norm_in0[i] = randGen();
			norm_in1[i] = randGen();

			input0[i] = LITP((THETYPE*)(&norm_in0[i]));
			input1[i] = LITP((THETYPE*)(&norm_in1[i]));
		}

		for (int i=0;i<test_size;i++)
		{
			output0[i] = input0[i] + input1[i];
		}

		for (int i=0;i<test_size;i++)
		{
			norm_out0[i] = norm_in0[i] + norm_in1[i];
		}

		int idx = 0;
		for (;idx<test_size;idx++)
		{
			const UINT32 r = output0[idx];

			if ( r != norm_out0[idx] )
			{
				cout<<"ADD:Error"<<endl;
				break;
			}
		}

		if (test_size==idx)
		{
			cout<<"ADD Test Pass"<<endl;
		}

		////////////////////////////////////
		// Test shift
		////////////////////////////////////
		THETYPE init_num0[] = {0x13,0x91,0x7B,0x0};
		THETYPE init_num1[] = {0x2A,0x71,0x6C,0x0};

		LITP tmp3(init_num0);
		LITP tmp2(init_num1);

		tmp3 = tmp3 + tmp2;

		std::bitset<LITP_BITS> bitBuffer;
		tmp3.debug_output_bitset(bitBuffer);
		cout<<bitBuffer<<endl;

		tmp3 = tmp3.left_shift_bits<9>();
		tmp3.debug_output_bitset(bitBuffer);
		cout<<bitBuffer<<endl;
	}

	/////////////////////////////////////////
	// test Subtraction
	/////////////////////////////////////////
	{
		typedef UCHAR8 THETYPE;

		typedef LargeIntegerMeta<THETYPE,4> LITP;
		const int LITP_BITS = LITP::TOTAL_BITS;

		vector<UINT32> norm_in0(test_size), norm_in1(test_size), norm_out0(test_size);
		vector<LITP> input0(test_size), input1(test_size), output0(test_size);

		for (int i=0;i<test_size;i++)
		{
			UINT32 a = randGen();
			UINT32 b = randGen();

			norm_in0[i] = max(a,b);
			norm_in1[i] = min(a,b);

			input0[i] = LITP((THETYPE*)(&norm_in0[i]));
			input1[i] = LITP((THETYPE*)(&norm_in1[i]));
		}

		for (int i=0;i<test_size;i++)
		{
			output0[i] = input0[i] - input1[i];
		}

		for (int i=0;i<test_size;i++)
		{
			norm_out0[i] = norm_in0[i] - norm_in1[i];
		}

		int idx = 0;
		for (;idx<test_size;idx++)
		{
			const UINT32 r = output0[idx];

			if ( r != norm_out0[idx] )
			{
				cout<<"SUB:Error"<<endl;
				break;
			}
		}

		if (test_size==idx)
		{
			cout<<"SUB Test Pass"<<endl;
		}
	}

	/////////////////////////////////////////
	// test multiplication
	/////////////////////////////////////////
	{
		typedef UCHAR8 THETYPE;

		vector<UINT64> mulT_in0(test_size), mulT_in1(test_size), mulT_res(test_size);

		const int test_mul_ele = 2;
		const UINT32 mask = ~(0xffffffff<<test_mul_ele);

		typedef LargeIntegerMeta<THETYPE, test_mul_ele> LI_MULT;
		typedef LargeIntegerMeta<THETYPE, 2 * test_mul_ele> LI_MULT_2;

		vector<LI_MULT> mulT_li0(test_size), mulT_li1(test_size);
		vector<LI_MULT_2> mulT_liRes(test_size);

		for (int i=0;i<test_size;i++)
		{
			mulT_in0[i] = randGen() & mask;
			mulT_in1[i] = randGen() & mask;

			mulT_li0[i] = LI_MULT((THETYPE*)(&mulT_in0[i]));
			mulT_li1[i] = LI_MULT((THETYPE*)(&mulT_in1[i]));
		}

		for (int i=0;i<test_size;i++)
		{
			mulT_liRes[i] = mulT_li0[i] * mulT_li1[i];
		}

		for (int i=0;i<test_size;i++)
		{
			mulT_res[i] = mulT_in0[i] * mulT_in1[i];
		}

		int idx = 0;
		for (;idx<test_size;idx++)
		{
			const UINT32 r = mulT_liRes[idx];

			if ( r != mulT_res[idx] )
			{
				cout<<"MUL:Error"<<endl;
				break;
			}
		}

		if (test_size==idx)
		{
			cout<<"MUL Test Pass"<<endl;
		}
	}

	UnitTest<Test_add<Test_LInt_LargeIntegerMeta<UINT32,4> > > add_tester(test_size);
	add_tester();

	UnitTest<Test_sub<Test_LInt_LargeIntegerMeta<UINT32,4> > > sub_tester(test_size);
	sub_tester();

	UnitTest<Test_mul<Test_LInt_LargeIntegerMeta<UINT32,4> > > mul_tester(test_size);
	mul_tester();
}